#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#include "debug.h"
#include "os_api.h"
#include "dev_api.h"
#include "ps_api.h"
#include "http.h"
#include "sockets.h"
#include "def.h"
#include "netdb.h"
#include "file_api.h"
#include "fota_api.h"

#define OEM_APPVER	"160X_V1.01"

#define _STACK_SIZE		(1024*3)

static void* _task_stack = NULL;

static OSTaskRef _task_ref = NULL;

static OSATimerRef _task_delete_timer_ref = NULL;

void DeleteTaskByTimer(UINT32 id)
{
    OSA_STATUS status;

    if(_task_ref)
    {
        status = OSATaskDelete(_task_ref);
        ASSERT(status == OS_SUCCESS);
        _task_ref = NULL;
	   
        if(_task_stack)
        {
            free(_task_stack);
            _task_stack = NULL;
        }
    }

    OSATimerDelete (_task_delete_timer_ref);
}


static void wait_network_ready(void)
{
	int count = 0;
	int ready=0;

	while (!ready){
		if(ps_GetNetworkReady() == TRUE){
			ready = 1;
		}
		LOG_PRINTF("wait_network_ready: %d s", count++);
		if (count > 300)
			dev_CtrlReset(0);
		
		OSATaskSleep(200);
	}
}

void fotaresultCallback(int result, char * msg, void * data)
{
	int *fota_result_ptr = (int *)data;
	*fota_result_ptr = result;
	LOG_PRINTF("fotaresultCallback result: %d, msg: %s", result, msg);
}

/*****************************************************************
* Function: lte_module_fota
*
* Description:
* 	该函数用于发起fota请求，支持app单独升级，以及差分升级，该差分升级中差分只针对system，即system的差分包+app的完整包。
* 
* Parameters:
* 	mode		[in] 	 0:FTP(默认不支持，需特殊sdk)，1：HTTP。
* 	url		  	[in] 	 fota文件对应的http url。
* 	username	[in] 	 http校验用户名，无用户名时填NULL或者""。
* 	password	[in] 	 http校验密码，无密码时填NULL或者""。
* 	delay_s	  	[in] 	 fota文件下载校验成功后延迟时间重启。
* 							0: 文件下载校验成功后不主动重启进入FOTA升级，任意时刻调用重启接口或者掉电重新上电后都会触发fota升级。
* 							>0: 文件下载校验成功后,指定时间重启进入fota升级， 单位：秒。
*
* 	fota_cb		[in] 	 fota_cb参数(int result, char *msg, void *data)：
*							result：
* 								0: 文件下载校验成功
* 								1: 服务器连接失败
* 								2: HTTP请求失败
* 								3: 下载的fota包校验失败
* 								4: 下载的fota包api检测失败
*                               	备注：该场景下参照fota说明，使用差分包升级（system+app），大部分场景无需升级sdk版本，特殊场景需要升级sdk对会遇到。
*                              		说明：app使用了cp不支持api，大部分情况是，随着sdk更新，新的app文件fota升级到已经烧录老版本的模块中，使用上面的差分包升级，使cp和system版本一致。
* 								5: 下载的fota包ver检测不匹配
*                               	备注：该场景下参照fota说明，使用差分包升级（system+app），大部分场景无需升级sdk版本，特殊场景需要升级sdk对会遇到。
*                               	说明：即app与cp版本不一致，api已检测，单独ver不匹配，大部分场景是可以直接升级app的，但是便于版本管理，还是建议使用上面的差分包升级，使cp和system版本一致。
* 						    	6: 其他错误
*							msg：
*								一些状态提示
*							data:
*								api传入最后一个参数
*
* 	data		[in] 	 传入回调函数的data参数：
* Return:
* 	0 执行成功
*	-1 执行失败
*
*****************************************************************/
int lte_module_fota(int mode, char *url, char *username, char *password, unsigned int delay_s, void (*fota_cb)(int, char *, void *), void *data);


#define GPS_BOOT_FILE		"CC11_Bootloader_APFLASH_AUTO.pkg"
#define GPS_FIRMWARE_FILE	"CC1167Q_Firmware.pkg"

static int detect_GPSmodule(void);

int connect_service_tcp(char *addr, char *portStr)
{
    int ret;
	int client_socket = -1;
    struct addrinfo hints;
    struct addrinfo *result, *rp;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = 0;
    hints.ai_protocol = 0;
    ret = getaddrinfo(addr, portStr, &hints, &result);
    if (ret != 0) {
        LOG_PRINTF("connect_service_tcp: resolve error\n");
        return -1;
    }

    for (rp = result; rp; rp = rp->ai_next) {
        client_socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (client_socket < 0) {
            LOG_PRINTF("connect_service_tcp: socket error\n");
            continue;
        }

        ret = connect(client_socket, rp->ai_addr, rp->ai_addrlen);
        if (ret < 0) {
            LOG_PRINTF("connect_service_tcp: connect error\n");
            close(client_socket);
            client_socket = -1;
            continue;
        } else {
            break;
        }
    }

	return client_socket;
}

static void _task(void *ptr)
{	
	int count = 60;
	int sock, ret;
	fd_set master, read_fds;
	int fdmax = 64;
	char buf[1024] = {0};	
	int fota_result = -1;

	UINT32 nvmFreeSize = 0;

	wait_network_ready();

	//detect_GPSmodule();

	sock = connect_service_tcp("122.51.209.56", "1111");

	LOG_FI_L_PRINTF("connect_service_tcp");
	LOG_FU_L_PRINTF("connect_service_tcp");
	LOG_L_PRINTF("connect_service_tcp");

	if (sock >= 0)
		LOG_PRINTF("connect sucess, wait recv the fota url\n");
again:
    ret = send(sock, "abcdefg", 7, 0);
    if (ret != 7) {
        LOG_PRINTF("send error\n");
        close(sock);
        return;
    }
	FD_ZERO(&master);
	FD_ZERO(&read_fds);
	FD_SET(sock, &master);
	read_fds = master;

	if(select(fdmax, &read_fds, NULL, NULL, NULL) == -1) {
		LOG_PRINTF("%s[%d], select error %d\n", __FUNCTION__, 0, lwip_getsockerrno(sock));
        close(sock);
        return;
	}
	memset(buf, 0x00, sizeof(buf));
    ret = recv(sock, buf, sizeof(buf) - 1, 0);
    if (ret <= 0) {
        LOG_PRINTF("recv error: %d\n", ret);
        close(sock);
        return;
    }
    buf[ret] = '\0';
    LOG_PRINTF("recv fota url: %s\n", buf);
    //close(sock);

	/*
	备注：	FDI_GetFreeSpaceSize(); 能查询当前nvm可用空间，
			如果不足以存放fota包的情况，可以使用nvm_remove_some_files()删除一些不常用配置文件（重新会重新生成默认的）释放20-30KB空间

			如果是HV9G模块，文件系统内部预置了GPS的固件（GPS芯片每次启动的时候需要加载固件），造成文件系统只有40KB左右的剩余
			FDI_remove(GPS_BOOT_FILE);
			FDI_remove(GPS_FIRMWARE_FILE);	

			如果此时fota包超过60KB，支持fota升级话，需要先删除GPS固件，升级后开机检测，如果不存在这两个文件的话，建议再通过HTTP下载这个两个文件保存到文件系统中
			参照 detect_GPSmodule();

			以上问题都是针对2MB flash的模块，4MB 或者 8MB 的模块falsh空间足够，基本无需这样处理。
	*/
	nvmFreeSize = FDI_GetFreeSpaceSize();
	LOG_PRINTF("nvmFreeSize: %d", nvmFreeSize);
	nvm_remove_some_files();

	fota_result = -1;
	ret = lte_module_fota(1, buf, NULL, NULL, 30, fotaresultCallback, &fota_result);
	if (ret != 0)
		goto again;

	while(fota_result == -1 && count--){
		OSATaskSleep(200);
	}
	LOG_PRINTF("..fota_result: %d\n", fota_result);

	if (fota_result != 0)
		goto again;
	
	OSATimerCreate(&_task_delete_timer_ref);
	OSATimerStart(_task_delete_timer_ref, 400, 0, DeleteTaskByTimer, 0);
}

int main(void)
{
    int ret;
	
	_task_stack = malloc(_STACK_SIZE);
    ASSERT(_task_stack != NULL);
	
    ret = OSATaskCreate(&_task_ref, _task_stack, _STACK_SIZE, 100, "_task", _task, NULL);
    ASSERT(ret == OS_SUCCESS);

    return 0;
}



#define HTTP_Addr	"http://43.143.124.203:9999/HTTP/"
#define HTTPServer_FILENAME_MAX		128

char HTTP_FILE_NAME[HTTPServer_FILENAME_MAX];

static void save_filesystem(char * data, int len, int num, void *cbdata)
{
	LOG_PRINTF("data = %s,len = %d,num = %d,cbdata =%s",data,len,num,cbdata);
	FILE_ID fd = FDI_fopen(HTTP_FILE_NAME,"ab");
	FDI_fseek(fd,0x00,SEEK_END);
	FDI_fwrite(data,len,1,fd);
	FDI_fclose(fd);
}

static int HTTP_Download(char *URL){

	UINT32 ret = TRUE;
	struct http_client * client = NULL;
	struct http_client_list * header = NULL;
	char dst[256];
	memset(dst,0x00,256);

	LOG_PRINTF("http dst [%s]",URL);
	sprintf(dst,"%s%s",URL,HTTP_FILE_NAME);
	LOG_PRINTF("http dst [%s],filename [%s]",URL,HTTP_FILE_NAME);
	/*Http download GPS files*/
	client = http_client_init();
	
	http_client_setopt(client, HTTPCLIENT_OPT_URL, dst);    /*set URL:support http/https and ip4/ip6*/
	http_client_setopt(client, HTTPCLIENT_OPT_HTTP1_0, 0);              /*"0" is HTTP 1.1, "1" is HTTP 1.0*/
	
	http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB, save_filesystem);     /*response callback*/
	http_client_setopt(client, HTTPCLIENT_OPT_METHOD, HTTPCLIENT_REQUEST_GET);  /*set method,support GET/POST/PUT*/
	http_client_setopt(client, HTTPCLIENT_OPT_POSTDATA, NULL);          /*post data is http context*/
	http_client_setopt(client, HTTPCLIENT_OPT_POSTLENGTH, 0);           /*http context length*/
	http_client_setopt(client, HTTPCLIENT_OPT_AUTH_TYPE, HTTP_AUTH_TYPE_BASE);  /*auth type support base and digest*/
	http_client_setopt(client, HTTPCLIENT_OPT_AUTH_USERNAME, "");           /*username*/
	http_client_setopt(client, HTTPCLIENT_OPT_AUTH_PASSWORD, "");           /*password*/

	// Add private HTTP header
	header = http_client_list_append(header, "Content-Type: application/octet-stream;\r\n");
	http_client_setopt(client, HTTPCLIENT_OPT_HTTPHEADER, header);

	http_client_getinfo(client, HTTPCLIENT_GETINFO_TCP_STATE, &ret);
	
	if(ret != HTTP_TCP_ESTABLISHED){
		LOG_PRINTF("HTTP  tcp not connecting");
		http_client_shutdown(client);
		return ret;
	}
	ret = http_client_perform(client);    /*execute http send and recv*/
	if(ret != HTTP_CLIENT_OK){
		http_client_shutdown(client); /*release http resources*/
		client = http_client_init();
	}
	if(header){
		http_client_list_destroy(header);
		header = NULL;
	}
	http_client_shutdown(client);
	return ret;
}

static int detect_GPSmodule(void)
{	
	LOG_PRINTF("func:%s",__FUNCTION__);
	UINT32 ret = TRUE;
	
	fatTYPE_stat CC11_Bootloader_stat= {0};
	fatTYPE_stat CC1167Q_Firmware_stat = {0};
	memset(&CC11_Bootloader_stat,0x00,sizeof(fatTYPE_stat));
	memset(&CC1167Q_Firmware_stat,0x00,sizeof(fatTYPE_stat));

	FDI_Stat(GPS_BOOT_FILE,&CC11_Bootloader_stat);
	FDI_Stat(GPS_FIRMWARE_FILE,&CC1167Q_Firmware_stat);
	if(0 != CC11_Bootloader_stat.st_size && 0 != CC1167Q_Firmware_stat.st_size ){
		LOG_PRINTF("GPSModule file is existed");
		return ret;
	}
	LOG_PRINTF("GPSModule is removed.Begin HTTP download");
	
	wait_network_ready();

	memset(HTTP_FILE_NAME,0x00,HTTPServer_FILENAME_MAX);
	strcpy(HTTP_FILE_NAME,GPS_BOOT_FILE);
	HTTP_Download(HTTP_Addr);

	memset(HTTP_FILE_NAME,0x00,HTTPServer_FILENAME_MAX);
	strcpy(HTTP_FILE_NAME,GPS_FIRMWARE_FILE);
	HTTP_Download(HTTP_Addr);

	LOG_PRINTF("End func:%s",__FUNCTION__);
	
	return ret;
}
